/**
 * @Author: Nicolana
 * @Time: 2021/09/03 19:11
 * @description 利用uni-app绘制一个简单的深度图，解决兼容问题
 */

function isObject(obj) {
  return Object.prototype.toString.call(obj) === "[object Object]";
}

function deepAssign(to, from) {
  for (let key in from) {
    if (
      !Object.prototype.hasOwnProperty.call(to, key) ||
      !isObject(from[key])
    ) {
      to[key] = from[key];
      continue;
    }
    if (isObject(from[key])) {
      deepAssign(to[key], from[key]);
    }
  }
  return to;
}

class DepthChart {
  constructor(options) {
    // context
    this.context = undefined;
    this.contextX = undefined;
    this.contextY = undefined;

    this.C_WIDTH = undefined;
    this.C_HEIGHT = undefined;
    this.WIDTH = undefined;
    this.HEIGHT = undefined;
    this.MAX_AMOUNT = undefined;
    this.BUY_LIST = [];
    this.SELL_LIST = [];
    this.H_SPACE = undefined;
    this.W_SPACE = undefined;
    this.ratio = 2; // 锐化
    options = options || {};
    this.options = deepAssign(
      {
        buy: {
          tooltip: {
            show: false,
            text: "",
          },
          line: {
            size: 1,
            color: "#28ab5d",
          },
          area: {
            color: "rgba(40,171,93, 0.2)",
          },
        },
        sell: {
          tooltip: {
            show: false,
            text: "",
          },
          line: {
            size: 1,
            color: "#fb4c51",
          },
          area: {
            color: "rgba(251,76,81, 0.2)",
          },
        },
        separator: {
          size: 1,
        },
        yAxis: {
          width: 50, // yAxis default width is 25px
          tooltip: {
            color: "#000000",
          },
        },
        xAxis: {
          height: 25,
          tooltip: {
            color: "#000000",
          },
        },
      },
      options
    );
  }

  init(id) {
    let myCanvasContainer = document.getElementById(id);
    let containerWidth = myCanvasContainer.clientWidth;
    let containerHeight = myCanvasContainer.clientHeight;
    myCanvasContainer.style.position = "relative";
    this.C_WIDTH = containerWidth;
    this.C_HEIGHT = containerHeight;

    // 创建主Canvas图
    let mainCanvas = document.createElement("canvas");
    mainCanvas.style.width = containerWidth + "px";
    mainCanvas.style.height =
      containerHeight - this.options.xAxis.height + "px";
    mainCanvas.width = containerWidth * this.ratio;
    mainCanvas.height =
      (containerHeight - this.options.xAxis.height) * this.ratio;
    this.WIDTH = containerWidth * this.ratio;
    this.HEIGHT = (containerHeight - this.options.xAxis.height) * this.ratio;
    myCanvasContainer.appendChild(mainCanvas);
    this.context = mainCanvas.getContext("2d");

    // 创建xAxisCanvas
    let xAxisCanvas = document.createElement("canvas");
    xAxisCanvas.style.postion = "absolute";
    xAxisCanvas.style.left = "absolute";
    xAxisCanvas.style.bottom = 0;
    xAxisCanvas.style.width = containerWidth + "px";
    xAxisCanvas.style.height = this.options.xAxis.height + "px";
    xAxisCanvas.width = containerWidth * this.ratio;
    xAxisCanvas.height = this.options.xAxis.height * this.ratio;
    myCanvasContainer.appendChild(xAxisCanvas);
    this.contextX = xAxisCanvas.getContext("2d");

    // 创建yAxisCanvas
    let yAxisCanvas = document.createElement("canvas");
    yAxisCanvas.style.position = "absolute";
    yAxisCanvas.style.left = 0;
    yAxisCanvas.style.top = 0;
    yAxisCanvas.style.zIndex = 100;
    yAxisCanvas.style.width = this.options.yAxis.width + "px";
    yAxisCanvas.style.height = this.HEIGHT + "px";
    yAxisCanvas.width = this.options.yAxis.width * this.ratio;
    yAxisCanvas.height = this.HEIGHT * this.ratio;
    myCanvasContainer.appendChild(yAxisCanvas);
    this.contextY = yAxisCanvas.getContext("2d");
  }

  setData(data) {
    this.BUY_LIST = data?.buy;
    this.SELL_LIST = data?.sell;
    const first_sell = this.SELL_LIST[0];
    const last_buy = this.BUY_LIST[this.BUY_LIST.length - 1];
    // 计算最大交易量，作为纵坐标的最大刻度
    this.MAX_AMOUNT = Math.max(first_sell.total, last_buy.total);
    this.H_SPACE = this.HEIGHT / 5; // 分成5份
    this.W_SPACE = this.WIDTH / 2 / this.BUY_LIST.length;
    this.BUY_LIST = this.formatBuyPath(this.BUY_LIST);
    this.SELL_LIST = this.formatSellPath(this.SELL_LIST);
    this.render();
  }

  formatBuyPath(list) {
    list.forEach((item, i) => {
      item.x =
        this.WIDTH / 2 -
        i * this.W_SPACE -
        this.options.separator.size * this.ratio;
      item.y = this.HEIGHT - (item.total / this.MAX_AMOUNT) * this.HEIGHT;
    });
    return list;
  }

  formatSellPath(list) {
    list.forEach((item, i) => {
      const index = this.SELL_LIST.length - i - 1;
      item.x =
        this.WIDTH / 2 +
        index * this.W_SPACE +
        this.options.separator.size * this.ratio;
      item.y = this.HEIGHT - (item.total / this.MAX_AMOUNT) * this.HEIGHT;
    });
    return list;
  }

  render() {
    this.context.clearRect(0, 0, this.WIDTH, this.HEIGHT);
    this.contextX.clearRect(
      0,
      0,
      this.WIDTH,
      this.options.xAxis.height * this.ratio
    );
    this.contextY.clearRect(
      0,
      0,
      this.options.yAxis.width * this.ratio,
      this.HEIGHT
    );
    this.drawAxis();
    this.drawDepth();
    this.drawLengend();
  }

  drawDepth() {
    this.drawBuyDepth();
    this.drawSellDepth();
  }

  drawBuyDepth() {
    // Draw Area
    let options = this.options.buy;
    this.context.beginPath();
    this.context.fillStyle = options.area.color;
    (this.context.lineWidth = options.line.size * this.ratio),
      (this.context.strokeStyle = options.line.color);
    this.context.moveTo(
      this.WIDTH / 2 - this.options.separator.size * this.ratio,
      this.HEIGHT
    );

    this.BUY_LIST.forEach((point, index) => {
      if (index > 0) {
        this.context.lineTo(point.x, this.BUY_LIST[index - 1].y);
      }
      this.context.lineTo(point.x, point.y);
    });
    this.context.lineTo(0, this.BUY_LIST[this.BUY_LIST.length - 1].y);
    this.context.stroke();
    this.context.lineTo(0, this.HEIGHT);
    this.context.lineTo(
      this.WIDTH / 2 - this.options.separator.size * this.ratio,
      this.HEIGHT
    );
    this.context.fill();
    this.context.closePath();
  }

  drawSellDepth() {
    // Draw Area
    let options = this.options.sell;
    this.context.beginPath();
    this.context.fillStyle = options.area.color;
    (this.context.lineWidth = options.line.size * this.ratio),
      (this.context.strokeStyle = options.line.color);
    this.context.moveTo(
      this.WIDTH / 2 + this.options.separator.size * this.ratio,
      this.HEIGHT
    );
    let sell_list = [...this.SELL_LIST].reverse();
    sell_list.forEach((point, index) => {
      if (index > 0) {
        this.context.lineTo(point.x, sell_list[index - 1].y);
      }
      this.context.lineTo(point.x, point.y);
    });
    this.context.lineTo(this.WIDTH, sell_list[sell_list.length - 1].y);
    this.context.stroke();
    this.context.lineTo(this.WIDTH, this.HEIGHT);
    this.context.lineTo(
      this.WIDTH / 2 + this.options.separator.size * this.ratio,
      this.HEIGHT
    );
    this.context.fill();
    this.context.closePath();
  }

  drawAxis() {
    this.drawAxisX();
    this.drawAxisY();
  }

  drawAxisX() {
    this.contextX.fillStyle = this.options.xAxis.tooltip.color;
    const buyLength = this.BUY_LIST.length;
    // const sellLength = this.SELL_LIST.length;

    const buyMax = this.BUY_LIST[buyLength - 1].price;
    const buyMin = this.BUY_LIST[0].price;
    const sellMax = this.SELL_LIST[0].price;
    const sellMin = this.SELL_LIST[this.SELL_LIST.length - 1].price;
    // 绘制中间值
    const CENTER_TEXT = (
      (Number(buyMax) + Number(sellMin)) /
      2
    ).toLocaleString();
    this.contextX.textAlign = "center";
    this.contextX.font = 12 * this.ratio + "px DIN-Medium";
    this.contextX.textBaseline = "top";
    this.contextX.fillText(CENTER_TEXT, this.WIDTH / 2, 5);

    // 绘制 Buy min
    const BUYMIN_TEXT = buyMin.toLocaleString();
    const BUY_TEXT_LENGTH = this.contextX.measureText(BUYMIN_TEXT).width;
    this.contextX.fillText(BUYMIN_TEXT, BUY_TEXT_LENGTH / 2, 5);

    // 绘制Sell Max
    const SELLMAX_TEXT = sellMax.toLocaleString();
    // const SELL_TEXT_LENGTH = this.contextX.measureText(SELLMAX_TEXT).width;
    this.contextX.textAlign = "right";
    this.contextX.fillText(SELLMAX_TEXT, this.WIDTH, 5);
  }

  getShortPrice(price) {
    if (price > 1e3) {
      price = (price / 1000).toLocaleString();
      return `${price}K`;
    }
    return Number(price).toLocaleString();
  }

  drawAxisY() {
    this.contextY.fillStyle = this.options.yAxis.tooltip.color;
    this.contextY.font = 12 * this.ratio + "px DIN-Medium";
    this.contextY.textAlign = "left";
    let seg = this.MAX_AMOUNT / 6;
    for (let i = 1; i < 7; i++) {
      const x = 0;
      const y = this.HEIGHT - ((seg * i) / this.MAX_AMOUNT) * this.HEIGHT;
      this.contextY.fillText(this.getShortPrice(seg * i), x, y);
    }
  }

  drawCircle(ctx, x, y, radius, color, lineWidth) {
    ctx.beginPath();
    ctx.arc(x, y, radius, 0, 2 * Math.PI);
    ctx.strokeStyle = color;
    ctx.lineWidth = lineWidth;
    ctx.stroke();
    ctx.closePath();
  }

  drawLengend() {
    // 画买盘tooltip
    this.context.save();
    this.context.fillStyle = this.options.yAxis.tooltip.color;
    this.context.font = 12 * this.ratio + "px DIN-Medium";
    this.context.textAlign = "right";
    const x = this.WIDTH / 2 - 10 * this.ratio;
    const y = 20 * this.ratio;
    const buyText = this.options.buy.tooltip.text;
    const buyTextWidth = this.context.measureText(buyText).width;
    this.context.fillText(buyText, x, y);
    this.drawCircle(
      this.context,
      x - buyTextWidth - 10 * this.ratio,
      y - 5 * this.ratio,
      4 * this.ratio,
      this.options.buy.line.color,
      1 * this.ratio
    );
    this.context.restore();

    // 画卖盘tooltip
    this.context.save();
    this.context.fillStyle = this.options.yAxis.tooltip.color;
    this.context.font = 12 * this.ratio + "px DIN-Medium";
    this.context.textAlign = "left";
    const sellX = this.WIDTH / 2 + 10 * this.ratio;
    const sellY = 20 * this.ratio;
    const sellText = this.options.sell.tooltip.text;
    const sellTextWidth = this.context.measureText(sellText).width;
    this.context.fillText(sellText, sellX, sellY);
    this.drawCircle(
      this.context,
      sellX + sellTextWidth + 10 * this.ratio,
      sellY - 5 * this.ratio,
      4 * this.ratio,
      this.options.sell.line.color,
      1 * this.ratio
    );
    this.context.restore();
  }
}

module.exports = DepthChart;
